/* Copyright 2008 Bas van den Berg
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "MudContainer.h"
#include "CmdVisitor.h"
#include "MudItem.h"
#include "MudKey.h"
#include "Error.h"
#include "MudPlayer.h"
#include "Room.h"
#include "Global.h"
#include "TaskQueue.h"
#include "MudObjectTask.h"
#include "myassert.h"
#include "StringBuilder.h"

using namespace Chronos;
using namespace std;

// ------------ MudContainer ------------

MudContainer::MudContainer(const string& name_, const string& descr_)
    : MudObject(name_, descr_)
{
    MudContainer::generateDescription();
}

VISITOR_ACCEPT(MudContainer)


const std::string& MudContainer::getDescription() const {
    return customDescription;
}


void MudContainer::generateDescription() {
    StringBuilder buffer;
    buffer << descr;
    if (!items.empty()) {
        buffer << " It contains:\n";
        for (ItemsConstIter iter = items.begin(); iter !=  items.end(); ++iter) {
            if (iter != items.begin()) buffer << '\n';
            buffer << "  " << (*iter)->getName();
        } 
    } else {
        buffer << " It's empty.";
    }
    customDescription = buffer;
}


bool MudContainer::canAccess(MudPlayer& player) {
    return !activateTrigger(MUDCONTAINER_TRY_ACCESS, (MudMobile*) &player);
}


void MudContainer::addItem(MudItem* item) {
	ItemContainer::addItem(item);
    generateDescription();
}


void MudContainer::removeItem(const MudItem* item) {
	ItemContainer::removeItem(item);
    generateDescription();
}

// ------------ MudOpenableContainer ------------

MudOpenableContainer::MudOpenableContainer(const string& name_, const string& descr_, unsigned int closeDelay_)
    : MudContainer(name_, descr_)
    , MudOpenable()
    , changed(false)
    , closeDelay(closeDelay_)
{
    ASSERT_NOT_EQUAL(0, closeDelay);
    MudOpenableContainer::generateDescription();
}

VISITOR_ACCEPT(MudOpenableContainer)


bool MudOpenableContainer::canAccess(MudPlayer& player) {
    if (!isOpen) {
        player.write("The " + getName() + " is closed.\n");
        return false;
    }
    return MudContainer::canAccess(player);
}


void MudOpenableContainer::open(MudPlayer& player) {
	if (isOpen) {
		player.write("The " + getName() + " is already open.\n");	
		return;
	}
    if (!canOpen(player)) return;

	isOpen = true;
    generateDescription();
	if (!changed) {
        Global<TaskQueue>()->addTask(new MudObjectFuncTask<MudOpenableContainer>(*this, &MudOpenableContainer::reset), closeDelay);
		changed = true;
	}
	player.write("OK\n");
	player.getRoom()->showText(player.getName() + " opens the " + getName() + ".", player);
}


void MudOpenableContainer::close(MudPlayer& player) {
	if (!isOpen) {
		player.write("The " + getName() + " is already closed.\n");	
		return;
	}
	isOpen = false;
    generateDescription();
	player.write("OK\n");
	player.getRoom()->showText(player.getName() + " closes the " + getName() + ".", player);
}


void MudOpenableContainer::generateDescription() {
    if (isOpen) MudContainer::generateDescription();
    else customDescription = descr + " It's closed.";
}


bool MudOpenableContainer::canOpen(MudPlayer& player) {
    return !activateTrigger(MUDOPENABLE_TRY_OPEN, (MudMobile*) &player);
}


unsigned int MudOpenableContainer::reset() {
    if (isOpen) {
        isOpen = false;	
        generateDescription();
        room->showText("The " + getName() + " closes.");
    }
    changed = false;
    return 0;
}

// ------------ MudLockedContainer ------------

MudLockedContainer::MudLockedContainer(const string& name_, const string& descr_, unsigned int closeDelay_)
    : MudOpenableContainer(name_, descr_, closeDelay_)
    , MudLockable()
{
    MudLockedContainer::generateDescription();
}


VISITOR_ACCEPT(MudLockedContainer)


void MudLockedContainer::generateDescription() {
    if (isLocked) customDescription = descr + " It's locked.";
    else MudOpenableContainer::generateDescription();
}


void MudLockedContainer::open(MudPlayer& player) {
    if (isLocked) {
		player.write("The " + getName() + " is locked.\n");	
		return;
    }
    MudOpenableContainer::open(player);
}


void MudLockedContainer::lock(MudPlayer& player, const MudKey& key) {
	if (isLocked) {
		player.write("It's already locked.\n");	
		return;
	}
    if (isOpen) {
        player.write("You should close it first.\n");
        return;
    }
    if (key.getID() != keyID) {
        player.write("It's not the right key.\n");
        return;
    }
//    if (!canChangeLock(player)) return;

	isLocked = true;
    generateDescription();
	player.write("OK\n");
	player.getRoom()->showText(player.getName() + " locks the " + getName() + ".", player);
}


void MudLockedContainer::unlock(MudPlayer& player, const MudKey& key) {
	if (!isLocked) {
		player.write("It's not locked.\n");	
		return;
	}
    if (key.getID() != keyID) {
        player.write("It's not the right key.\n");
        return;
    }
//    if (!canChangeLock(player)) return;

	isLocked = false;
    generateDescription();
	if (!changed) {
        Global<TaskQueue>()->addTask(new MudObjectFuncTask<MudLockedContainer>(*this, &MudLockedContainer::reset), closeDelay);
		changed = true;
	}
	player.write("OK\n");
	player.getRoom()->showText(player.getName() + " unlocks the " + getName() + ".", player);
}


unsigned int MudLockedContainer::reset() {
    MudOpenableContainer::reset();
    if (!isLocked) isLocked = true;

    generateDescription();
    changed = false;
    return 0;
}

